
const path          = require('path');
const fs            = require('fs');

const async         = require('async');
const nodemailer    = require('nodemailer');
const sendcloud     = require('sendcloud');
const Mailchimp     = require('mailchimp-api-v3');

const BaseService   = require('./base-service').BaseService;

const {Configuration} = require('../constants');

class MailService extends BaseService {

    get mailTemplates() {
        if (!this._mailTemplates) {
            this._mailTemplates = new Map();
        }

        return this._mailTemplates;
    }

    get SMTPClient() {
        return this._smtpTransporter;
    }

    get mailchimpClient() {
        if (!this._mailchimpClient) {
            this._mailchimpClient = new Mailchimp(this.mailchimpConfig.key);
        }

        return this._mailchimpClient;
    }

    get mailConfig() {
        return this.getApplicationConfiguration(Configuration.MAIL);
    }

    get sendcloudConfig() {
        return this.getApplicationConfiguration(Configuration.SEND_CLOUD);
    }

    get mailchimpConfig() {
        return this.getApplicationConfiguration(Configuration.MAILCHIMP);
    }

    /**
     * Setup the SMTP client for sending e-mail.
     * @name setupSMTPClient
     * 
     * @memberOf MailService
     */
    setupSMTPClient() {
        let mailSMTPConfig = this.mailConfig.smtp;

        this._smtpTransporter = nodemailer.createTransport({
            pool: mailSMTPConfig.pool,
            host: mailSMTPConfig.host,
            auth: {
                user: mailSMTPConfig.user,
                pass: mailSMTPConfig.password
            },
            secure: true,
            authMethod: mailSMTPConfig.authMethod
        });
    }

    /**
     * Send mail with template
     * 
     * @name sendMailByTemplate
     * @param {String} template - The template name or path
     * @param {Object} options - The mail options
     * @param {Object} values - The values need to replace
     * 
     * @returns {void}
     */
    sendMailByTemplate(template, options, values, parentCallback) {
        var me = this;

        parentCallback = typeof parentCallback === 'function' ? parentCallback : (error, info) => {};

        let mailConfig = this.mailConfig;
        let mailOptions = {
            from: options.from || mailConfig.from,
            to: options.to,
            cc: options.cc,
            subject: options.subject,
            html: this.buildMailContentByTemplate(template, values)
        };
        async.waterfall([
            (callback) => { // Try to send email via SMTP 
                me.SMTPClient.sendMail(mailOptions, (error, info) => {
                    if (error) {
                        me.logger.error('Error happenend when send mail, ', error);
                    } else {
                        me.logger.info("WebRequestError email send successfully. ", info);
                    }
                    callback(null, error, info);
                });
            },
            (error, info, callback) => { // If send email failed via SMTP, use sendcloud API to send email
                if (error) {
                    let sendcloudConfig = me.sendcloudConfig;
                    let sendcloudClient = new sendcloud(sendcloudConfig.api_user, sendcloudConfig.api_key, mailOptions.from, '跨屏产品团队');
                    let sendcloudOptions = {};
                    sendcloudOptions.cc = mailOptions.cc;
                    sendcloudClient.send(mailOptions.to, mailOptions.subject, mailOptions.html, sendcloudOptions).then((info) => {
                        if (info.message === 'success') {
                            parentCallback(null, info);
                        } else {
                            me.logger.error('Sending mail failed, ', new Error(info.message));
                            parentCallback(new Error(info.message), null);
                        }
                    });
                } else {
                    parentCallback(error, info);
                }
            }
        ]);
    }

    /**
     * Build mail content via template and values
     * 
     * @name buildMailContentByTemplate
     * @param {String} template - The template id
     * @param {Object} values - The key/value
     * 
     * @returns {String}
     */
    buildMailContentByTemplate(template, values) {
        var templateContent = this.mailTemplates.get(template);

        var templateFile = path.resolve(this.Context.getCurrentWorkDirectory(), template);

        if (!templateContent && fs.existsSync(templateFile)) {
            templateContent = fs.readFileSync(templateFile, {
                encoding: 'utf-8'
            });

            this.mailTemplates.set(template, templateContent);
        }

        if (templateContent) {
            for(var key in values) {
                let regex = new RegExp("\\$\\{" + key + "\\}", 'gi');
                let value = values[key];
                templateContent = templateContent.replace(regex, value);
            }

            return templateContent;
        } else {
            var items = [];
            for( var key in values) {
                items.push(`<div><b>${key}</b>: <span>${values[key]}</span></div>`)
            }
            templateContent = items.join('');
            return `<html><body>${templateContent}</body></html>`
        }

    }

    /**
     * Add member to mailchimp mail list
     * 
     * @name addMemberToMailchimpList
     * @param {String} listId - The list id
     * @param {String} email - The email address
     * @param {String} status - The subscribe status, possible values: subscribed, unsubscribed, cleaned, pending
     * @param {Object} mergefields - The user customized fields
     * 
     * @returns {void}
     */
    addMemberToMailchimpList(listId, email, status, mergefields, parentCallback) {

        if (!this.mailchimpConfig.enabled) return;

        listId = listId || this.mailchimpConfig.list.id;

        this.mailchimpClient.post(`/lists/${listId}/members`, {
            email_address: email,
            status: status,
            merge_fields: mergefields
        }, (error, result) => {
            parentCallback(error, result);
        });
    }

    constructor(context) {
        super(context);

        this.setupSMTPClient();
    }
}

exports.MailService = MailService;